﻿/*
 * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
 *
 * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
 *
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
 */

#ifndef SEMAPHORE_H_
#define SEMAPHORE_H_

/*
 * 目前发现信号量在32位的系统上有问题，
 * 休眠的线程无法被正常唤醒，先禁用之
 * Currently, it is found that semaphores have issues on 32-bit systems,
 * sleeping threads cannot be normally woken up, disable them for now
 */
#if defined(__linux__)
#include <semaphore.h>
#define HAVE_SEM
#endif //HAVE_SEM

#include <mutex>
#include <condition_variable>

namespace toolkit {

/**
 * 信号量类，用于线程同步
 * 在32位系统上存在问题，已禁用原生信号量实现
 * 使用互斥锁和条件变量实现替代方案
 */
class semaphore {
public:
    /**
     * 构造函数，初始化信号量
     * @param initial 初始计数值，默认为0
     */
    explicit semaphore(size_t initial = 0) {
#if defined(HAVE_SEM)
        sem_init(&_sem, 0, initial);  // 在Linux系统上初始化原生信号量
#else
        _count = initial;  // 在其他系统上初始化计数值
#endif
    }

    /**
     * 析构函数，清理资源
     */
    ~semaphore() {
#if defined(HAVE_SEM)
        sem_destroy(&_sem);  // 在Linux系统上销毁原生信号量
#endif
    }

    /**
     * 增加信号量计数值
     * @param n 增加的计数值，默认为1
     */
    void post(size_t n = 1) {
#if defined(HAVE_SEM)
        while (n--) {
            sem_post(&_sem);  // 在Linux系统上增加原生信号量
        }
#else
        std::unique_lock<std::recursive_mutex> lock(_mutex);  // 获取互斥锁
        _count += n;  // 增加计数值
        if (n == 1) {
            _condition.notify_one();  // 如果只增加1，唤醒一个等待线程
        } else {
            _condition.notify_all();  // 如果增加多个，唤醒所有等待线程
        }
#endif
    }

    /**
     * 等待信号量，当计数值大于0时返回
     * 否则阻塞当前线程直到计数值大于0
     */
    void wait() {
#if defined(HAVE_SEM)
        sem_wait(&_sem);  // 在Linux系统上等待原生信号量
#else
        std::unique_lock<std::recursive_mutex> lock(_mutex);  // 获取互斥锁
        while (_count == 0) {
            _condition.wait(lock);  // 如果计数值为0，等待条件变量
        }
        --_count;  // 计数值减1
#endif
    }

private:
#if defined(HAVE_SEM)
    sem_t _sem;  // Linux系统上的原生信号量对象
#else
    size_t _count;  // 信号量计数值
    std::recursive_mutex _mutex;  // 递归互斥锁，允许同一线程多次获取
    std::condition_variable_any _condition;  // 条件变量，用于线程阻塞和唤醒
#endif
};

} /* namespace toolkit */
#endif /* SEMAPHORE_H_ */